/*************************************************************************
  Crytek Source File.
  Copyright (C), Crytek Studios, 2001-2004.
 -------------------------------------------------------------------------
  $Id$
  $DateTime$
  Description: Loads levels, gathers level info.
  
 -------------------------------------------------------------------------
  History:
  - 17:8:2004   18:00 : Created by Marcio Martins

*************************************************************************/
#ifndef __LEVELSYSTEM_H__
#define __LEVELSYSTEM_H__

#if _MSC_VER > 1000
# pragma once
#endif

#include "ILevelSystem.h"

class CLevelInfo :
	public ILevelInfo
{
	friend class CLevelSystem;
public:
	CLevelInfo(): m_heightmapSize(0), m_bMetaDataRead(false), m_isModLevel(false) {};

	// ILevelInfo
	VIRTUAL const char *GetName() const { return m_levelName.c_str(); };
	VIRTUAL const char *GetPath() const { return m_levelPath.c_str(); };
	VIRTUAL const char *GetPaks() const { return m_levelPaks.c_str(); };
	VIRTUAL const char* GetDisplayName()const;
	VIRTUAL bool GetIsModLevel() const{ return m_isModLevel; }
	VIRTUAL const ILevelInfo::TStringVec& GetMusicLibs() const { return m_musicLibs; };
	VIRTUAL int GetHeightmapSize() const { return m_heightmapSize; };
	VIRTUAL const bool MetadataLoaded() const { return m_bMetaDataRead; }

	VIRTUAL int GetGameTypeCount() const { return m_gameTypes.size(); };
	VIRTUAL const ILevelInfo::TGameTypeInfo *GetGameType(int gameType) const { return &m_gameTypes[gameType]; };
	VIRTUAL bool SupportsGameType(const char *gameTypeName) const;
	VIRTUAL const ILevelInfo::TGameTypeInfo *GetDefaultGameType() const;
	VIRTUAL const char* GetDefaultGameRules() const{ return m_gamerules.empty() ? NULL : m_gamerules[0].c_str(); }
	VIRTUAL ILevelInfo::TStringVec GetGameRules() const{ return m_gamerules; }
	VIRTUAL bool HasGameRules() const{ return !m_gamerules.empty(); }

	VIRTUAL const SMinimapInfo& GetMinimapInfo() const { return m_minimapInfo; }

	VIRTUAL bool GetAttribute(const char* name, TFlowInputData& val) const;
	// ~ILevelInfo


	void GetMemoryUsage(ICrySizer * ) const;
	
private:
  void        ReadMetaData();
	bool        ReadInfo();

	bool        OpenLevelPak();
	void        CloseLevelPak();

	string			m_levelName;
	string			m_levelPath;
	string			m_levelPaks;
	string      m_levelDisplayName;
	TStringVec	m_musicLibs;
  TStringVec  m_gamerules;
	int					m_heightmapSize;
	bool				m_bMetaDataRead;
	std::vector<ILevelInfo::TGameTypeInfo> m_gameTypes;
	bool        m_isModLevel;
	SMinimapInfo m_minimapInfo;
	typedef std::map< string, TFlowInputData, stl::less_stricmp<string> > TAttributeList;
	TAttributeList m_levelAttributes;
};


class CLevelRotation : public ILevelRotation
{
public:
	CLevelRotation();
	virtual ~CLevelRotation();

	typedef struct SLevelRotationEntry
	{
		string levelName;
		string gameRulesName;
    std::vector<string> settings;
	} SLevelRotationEntry;

	typedef std::vector<SLevelRotationEntry> TLevelRotationVector;

	// ILevelRotation
  VIRTUAL bool Load(ILevelRotationFile* file);
	VIRTUAL bool Save(ILevelRotationFile* file);
	
	VIRTUAL void Reset();
	VIRTUAL int  AddLevel(const char *level, const char *gamerules);
  VIRTUAL void AddSetting(int level, const char* setting);

	VIRTUAL bool First();
	VIRTUAL bool Advance();
	VIRTUAL const char *GetNextLevel() const;
	VIRTUAL const char *GetNextGameRules() const;

  VIRTUAL int  GetNextSettingsNum() const;
  VIRTUAL const char *GetNextSetting(int idx); 

	VIRTUAL int GetLength() const;
	VIRTUAL int GetNext() const;

  VIRTUAL void SetRandom(bool rnd);
  VIRTUAL bool IsRandom()const;

  VIRTUAL void ChangeLevel(IConsoleCmdArgs* pArgs = NULL);
	//~ILevelRotation

	VIRTUAL void    Shuffle(int nSeed);

	bool LoadFromXmlRootNode(const XmlNodeRef rootNode, const char* altRootTag);


	ILINE void SetExtendedInfoId(const ILevelRotation::TExtInfoId id) { m_extInfoId = id; }
	ILINE ILevelRotation::TExtInfoId  GetExtendedInfoId() { return m_extInfoId; }

protected:
	TLevelRotationVector m_rotation;
  bool                 m_random;
	int                  m_next;
  std::vector<int>     m_shuffle;
	ILevelRotation::TExtInfoId  m_extInfoId;

};


class CLevelSystem :
	public ILevelSystem,
	public ISystem::ILoadingProgressListener
{
public:
	CLevelSystem(ISystem *pSystem, const char *levelsFolder);
	virtual ~CLevelSystem();

	void Release() { delete this; };

	// ILevelSystem
	VIRTUAL void Rescan(const char *levelsFolder);
  VIRTUAL void LoadRotation();
	VIRTUAL int GetLevelCount();
	VIRTUAL ILevelInfo *GetLevelInfo(int level);
	VIRTUAL ILevelInfo *GetLevelInfo(const char *levelName);

	VIRTUAL void AddListener(ILevelSystemListener *pListener);
	VIRTUAL void RemoveListener(ILevelSystemListener *pListener);

	VIRTUAL ILevel *GetCurrentLevel() const { return m_pCurrentLevel;	}
	VIRTUAL ILevel *LoadLevel(const char *levelName);
	VIRTUAL void UnLoadLevel();
	VIRTUAL ILevel *SetEditorLoadedLevel(const char *levelName);
	VIRTUAL void PrepareNextLevel(const char *levelName);
	VIRTUAL float GetLastLevelLoadTime() { return m_fLastLevelLoadTime; };
	VIRTUAL bool IsLevelLoaded() { return m_bLevelLoaded; }

	VIRTUAL ILevelRotation* GetLevelRotation() { return &m_levelRotation; };

	VIRTUAL ILevelRotation* FindLevelRotationForExtInfoId(const ILevelRotation::TExtInfoId findId);

	VIRTUAL bool AddExtendedLevelRotationFromXmlRootNode(const XmlNodeRef rootNode, const char* altRootTag, const ILevelRotation::TExtInfoId extInfoId);
	VIRTUAL void ClearExtendedLevelRotations();
	VIRTUAL ILevelRotation *CreateNewRotation(const ILevelRotation::TExtInfoId id);
	// ~ILevelSystem

	// ILevelSystemListener
	virtual void OnLevelNotFound(const char *levelName);
	virtual void OnLoadingStart(ILevelInfo *pLevel);
	virtual void OnLoadingComplete(ILevel *pLevel);
	virtual void OnLoadingError(ILevelInfo *pLevel, const char *error);
	virtual void OnLoadingProgress(ILevelInfo *pLevel, int progressAmount);
	virtual void OnUnloadComplete(ILevel* pLevel);
	// ~ILevelSystemListener

	// ILoadingProgessListener
	virtual void OnLoadingProgress(int steps);
	// ~ILoadingProgessListener

	void PrecacheLevelRenderData();
	void GetMemoryUsage(ICrySizer * s) const;

	void SaveOpenedFilesList();

private:
	// lowercase string and replace backslashes with forward slashes
	// TODO: move this to a more general place in CryEngine
	string&	UnifyName(string&	name);
	void ScanFolder(const char *subfolder, bool modFolder);
	void LogLoadingTime();
	bool LoadLevelInfo( CLevelInfo &levelInfo );
	void LoadLevelEntityPrototypes(CLevelInfo* pLevelInfo);

	// internal get functions for the level infos ... they preserve the type and don't
	// directly cast to the interface
	CLevelInfo *GetLevelInfoInternal(int level);
	CLevelInfo *GetLevelInfoInternal(const char *levelName);

	typedef std::vector<CLevelRotation>  TExtendedLevelRotations;

	ISystem									*m_pSystem;
	std::vector<CLevelInfo> m_levelInfos;
	string									m_levelsFolder;
	ILevel									*m_pCurrentLevel;
	ILevelInfo							*m_pLoadingLevelInfo;

	TExtendedLevelRotations	m_extLevelRotations;
	CLevelRotation					m_levelRotation;

	string                  m_lastLevelName;
	float                   m_fLastLevelLoadTime;
	float										m_fFilteredProgress;
	float										m_fLastTime;

	bool                    m_bLevelLoaded;
	bool                    m_bRecordingFileOpens;

	int                     m_nLoadedLevelsCount;

	CTimeValue              m_levelLoadStartTime;

	static int							s_loadCount;

	std::vector<ILevelSystemListener *> m_listeners;
};

#endif //__LEVELSYSTEM_H__
